/**
 * Copyright 2010 Kurtis Nusbaum
 *
 * This file is part of RemoteRunner.  
 *
 * RemoteRunner is free software: you can 
 * redistribute it and/or modify it under the terms of the GNU General Public 
 * License as published by the Free Software Foundation, either version 2 of the 
 * License, or (at your option) any later version.  
 *
 * RemoteRunner is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
 * for more details.  You should have received a copy of the GNU  General 
 * Public License along with RemoteRunner. If not, see 
 * http://www.gnu.org/licenses/.
 */

package org.klnusbaum.remoterunner;

import android.app.ListActivity;
import android.app.AlertDialog;
import android.content.Intent;
import android.content.Intent.ShortcutIconResource;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.os.Bundle;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.SimpleCursorAdapter;
import android.util.Log;
import android.database.Cursor;
import android.content.Context;
import android.preference.PreferenceManager;
import android.content.SharedPreferences;

import org.klnusbaum.aboutlib.AboutActivity;

import java.util.HashMap;

/**
 * Class displaying a list of all host.
 * Users can select a host to command, add a new host, or edit and existing one.
 */
public class HostListActivity extends ListActivity implements 
	DialogInterface.OnClickListener, DialogInterface.OnDismissListener
{
  
	/** Tag used for logging */
  private static final String TAG = "HostConf";

	/**
	 * Constants used for identifying Extras in Intents.
	 */
	public static final String EXTRA_HOSTID = "hostid";

	/**
	 * Constants for Intent Actions
	 */
	private static final String ACTION_STARTHOST = "starthost";

	/** Current EditHostDialog that is showing. 
		If there isn't one this is null. */
	private EditHostDialog currentHostDialog = null;

	/** Current confirm delete dialog that is showing.
		If there isn't one this is null. */
	private AlertDialog currentConfirmDeleteDialog = null;

	/**
	 * Current host trying to be deleted. HostsDB.INVALID_HOST_ID if
	 * no host is being currently deleted.
	 */
	private int currentHostToBeDeleted = HostsDB.INVALID_HOST_ID;

	/** Constants for context menu */
  private static final int EDIT_ID = Menu.FIRST;
  private static final int DELETE_ID = Menu.FIRST + 1;

	/** Database used for storing information about hosts. */
  private HostsDB hostsDatabase;

	/** Adapter used for ListView */
	private HostsAdapter hostsAdapter;
	/** Cursor pointing to a list of all hosts. */
	private Cursor hostsCursor;

	/** Whether or not we're just picking a host to use as a shortcut. */
	private boolean makingShortcut;

	/** Constants for helping save dialog state */
	private static final String EXTRA_DIALOG_HOST_ID = "DIALOG_HOST_ID";
	private static final String EXTRA_HOST_DIALOG_STATE = "HOST_DIALOG_STATE";
	private static final String EXTRA_CONFIRM_DELETE_STATE = 
		"CONFIRM_DELETE_STATE";
	private static final String EXTRA_HOST_BEING_DELETED = "HOST_BEING_DELETED";
	
	@Override
  public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    hostsDatabase = new HostsDB(this);
		hostsCursor = hostsDatabase.getHosts();
		hostsAdapter = new HostsAdapter(
      this, 
      R.layout.host_list_item,
			hostsCursor,
			new String[] {HostsDB.COLUMN_NAME, HostsDB.COLUMN_HOSTNAME},
			new int[] {R.id.name, R.id.hostname}
    );
    setListAdapter(hostsAdapter);

    ListView lv = getListView();
    lv.setTextFilterEnabled(true);

		this.makingShortcut = 
			Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction());

    lv.setOnItemClickListener(new OnItemClickListener() {
      public void onItemClick(AdapterView<?> parent, View view,
        int position, long id)
      {
				if(makingShortcut){
					ShortcutIconResource icon = 
						Intent.ShortcutIconResource.fromContext(
							HostListActivity.this, R.drawable.launcher);

						Intent hostIntent = new Intent();
						hostIntent.putExtra(
							EXTRA_HOSTID, 
							((HostsAdapter)parent.getAdapter()).getHostID(position));
						hostIntent.setClass(
							HostListActivity.this, CommandHostActivity.class);
						
						Intent shortcutIntent = new Intent(Intent.ACTION_VIEW);
						shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, hostIntent);
						shortcutIntent.putExtra(
							Intent.EXTRA_SHORTCUT_NAME, 
							((HostsAdapter)parent.getAdapter()).getName(position));
						shortcutIntent.putExtra(
							Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
							icon);
						setResult(RESULT_OK, shortcutIntent);
						finish();
				}
				else{
        	startCommandingHost(
						((HostsAdapter)parent.getAdapter()).getHostID(position));
				}
      }
    });
   	registerForContextMenu(lv); 
  }

	@Override
	protected void onDestroy(){
		super.onDestroy();
		hostsCursor.close();
		hostsDatabase.close();
	}

	@Override
	protected void onSaveInstanceState(Bundle outState){
		super.onSaveInstanceState(outState);
		if(currentHostDialog != null){
			outState.putInt(EXTRA_DIALOG_HOST_ID, currentHostDialog.getHostID());
			outState.putBundle(
				EXTRA_HOST_DIALOG_STATE, currentHostDialog.onSaveInstanceState());
			currentHostDialog.cancel();
		}
		else if(currentConfirmDeleteDialog !=null){
			outState.putInt(
				EXTRA_HOST_BEING_DELETED, 
				currentHostToBeDeleted);
			currentConfirmDeleteDialog.dismiss();
		}
	}	
	
	@Override
	protected void onRestoreInstanceState(Bundle savedInstanceState){
		super.onRestoreInstanceState(savedInstanceState);
		if(savedInstanceState != null && 
			savedInstanceState.keySet().contains(EXTRA_HOST_DIALOG_STATE))
		{
			currentHostDialog = new EditHostDialog(
				this, 
				this, 
				this,
				savedInstanceState.getInt(EXTRA_DIALOG_HOST_ID), 
				hostsDatabase);
			currentHostDialog.onRestoreInstanceState(
				savedInstanceState.getBundle(EXTRA_HOST_DIALOG_STATE));
			currentHostDialog.show();
		}
		else if(savedInstanceState != null &&
			savedInstanceState.keySet().contains(EXTRA_HOST_BEING_DELETED))
		{
			showConfirmDeleteDialog(
				savedInstanceState.getInt(EXTRA_HOST_BEING_DELETED));
		}
	}
		

  @Override
  public boolean onCreateOptionsMenu(Menu menu){
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.host_conf_menu, menu);
    return true;
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case R.id.menuAddHost:
			currentHostDialog = new EditHostDialog(this, this, this, hostsDatabase);
			currentHostDialog.show();
      return true;
		case R.id.menuAbout:
			Intent showAboutIntent =
				new Intent(HostListActivity.this, AboutActivity.class);
			startActivity(showAboutIntent);
			return true;
		case R.id.menuPrefs:
			Intent prefIntent = new Intent(this, XMLPreferencesActivity.class);
			startActivity(prefIntent);
    default:
      return super.onOptionsItemSelected(item);
    }
  }

  @Override
  public void onCreateContextMenu(ContextMenu menu, View v,
    ContextMenuInfo menuInfo)
  {
		super.onCreateContextMenu(menu, v, menuInfo);
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.host_context_menu, menu);
		menu.setHeaderTitle(R.string.host_actions);
  }

  @Override
  public boolean onContextItemSelected(MenuItem item){
    AdapterContextMenuInfo info = 
      (AdapterContextMenuInfo)item.getMenuInfo();
    int hostToModify =
      ((HostsAdapter)getListAdapter()).getHostID(info.position);
    switch(item.getItemId()){
    case R.id.delete_host:
			SharedPreferences prefs = 
				PreferenceManager.getDefaultSharedPreferences(this);
			if(prefs.getBoolean(getString(R.string.host_delete_confirm_key), true)){
				showConfirmDeleteDialog(hostToModify);
			}
			else{
				deleteHost(hostToModify);
			}
      return true;
		case R.id.edit_host:
			currentHostDialog = 
				new EditHostDialog(this, this, this, hostToModify, hostsDatabase);
			currentHostDialog.show();
			return true;
    default:
      return super.onContextItemSelected(item);
    }
  }
      
	/**
	 * Start issuing a commands to a host identified by the hostID.
	 *
	 * @param hostID The id of the host to start commanding.
	 */
  private void startCommandingHost(int hostID){
    Intent hostIntent = 
      new Intent(HostListActivity.this, CommandHostActivity.class);
    hostIntent.putExtra(EXTRA_HOSTID, hostID);
    startActivity(hostIntent);
  }

	/**
	 * Add a host to the list of available hosts.
	 *
	 * @param hostname Name of the host.
	 * @param username Username used to login to the host.
	 * @param portNumber Porn number used to login to the host.
	 * @param password Password used to login to the host.
	 *
	 * @return Whether or not the add was successful.
	 */
  private boolean addHost(String name, String hostname, String username){
    if(hostsDatabase.addHost(name, hostname, username) < 0){
      Log.e(TAG, "Error adding host to hostsDatabase");
			Toast.makeText(this, "Error: host not added",
			Toast.LENGTH_SHORT).show();
			return false;
		}
		hostsAdapter.refresh();
		return true;
  }

	/**
	 * Update a hosts information.
	 * 
	 * @param hostID ID of ths host to update.
	 * @param hostname New name of the host.
	 * @param username New username to be used with the host.
	 * @param password New password used for the host.
	 * @param portNumber New port number to be used for logging into the host.
	 */
	private void updateHost(int hostID, String name, String hostname, 
		String username)
	{
		if(hostsDatabase.updateHost(hostID, name, hostname, username) <= 0){
			Log.e(TAG, "Error modifying host in hostsDatabase.");
			Toast.makeText(this, "Error: host not modified",
			Toast.LENGTH_SHORT).show();
		}
		else{
			hostsAdapter.refresh();
		}
	}

	/**
	 * Delete a host.
	 *
	 * @param hostID ID of the host to delete.
	 */
  private void deleteHost(int hostID){
    if(hostsDatabase.deleteHost(hostID) >= 0){
			hostsAdapter.refresh();
		}
		else{
			Toast.makeText(
				this, "Error: host not deleted", Toast.LENGTH_SHORT).show();
			Log.e(TAG, "Error deleting host from hostsDatabase.");
		}
  }

	@Override
	public void onClick(DialogInterface dialogInterface, int button){
		if(button == DialogInterface.BUTTON_POSITIVE){
			EditHostDialog editDialog = (EditHostDialog)dialogInterface;
			if(editDialog.isEditing()){
				updateHost(
					editDialog.getHostID(),
					editDialog.getName(),
					editDialog.getHostname(),
					editDialog.getUsername());
			}
			else{
				addHost(
					editDialog.getName(),
					editDialog.getHostname(),
					editDialog.getUsername());
			}
			editDialog.dismiss();
		}	
		else if(button == DialogInterface.BUTTON_NEGATIVE){
			dialogInterface.cancel();
		}
	}

	@Override
	public void onDismiss(DialogInterface dialog){
		currentHostDialog = null;
	}

	public void showConfirmDeleteDialog(int hostToBeDeleted){
		currentHostToBeDeleted = hostToBeDeleted;
		final DialogInterface.OnClickListener confirmDeleteListener =
			new DialogInterface.OnClickListener(){
				public void onClick(DialogInterface dialog, int which){
					if(which == AlertDialog.BUTTON_POSITIVE){
						deleteHost(currentHostToBeDeleted);
					}
					dialog.dismiss();
				}
			};
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setTitle(R.string.delete_host)
		.setMessage(R.string.delete_host_message)
		.setPositiveButton(android.R.string.ok, confirmDeleteListener)
		.setNegativeButton(android.R.string.cancel, confirmDeleteListener);
		currentConfirmDeleteDialog = builder.create();
		currentConfirmDeleteDialog.setOnDismissListener(
			new DialogInterface.OnDismissListener(){
				public void onDismiss(DialogInterface dialog){
					currentConfirmDeleteDialog = null;
					currentHostToBeDeleted = HostsDB.INVALID_HOST_ID;
				}
			});
		currentConfirmDeleteDialog.show();
	}
			
 
	/**
	 * Adapter used to display a list of Hosts.
	 */
	public class HostsAdapter extends SimpleCursorAdapter{

		/**
		 * Constructor for creating a HostsAdapter.
		 * @param context Context in which the adapter is being used.
		 * @param layout Layout to be used for displaying the hosts.
		 * @param c The Cursor used to get the hosts information.
		 * @param from List of curor columns to be displayed.
		 * @param to List of layout itmes where the columns from the cursor should 
				be displayed.
		 */
		public HostsAdapter(Context context, int layout, Cursor c, 
			String[] from, int[] to)
		{
			super(context, layout, c, from, to);
		}

		/**
		 * Get the host id of a host located at a position in the list.
		 *
		 * @param position Position in the list of the desired host.
		 * @return The id of the host located at the position in the list.
	 	 */
		public int getHostID(int position){
			Cursor cursorAtPos = (Cursor)getItem(position);
			return cursorAtPos.getInt(cursorAtPos.getColumnIndex(HostsDB.KEY_ID));
		}

		/**
		 * Get the host name of a host located at a position in the list.
		 *
		 * @param position Position in the list of the desired host.
		 * @return The hostname of the host located at the position in the list.
	 	 */
		public String getHostname(int position){
			Cursor cursorAtPos = (Cursor)getItem(position);
			return cursorAtPos.getString(
				cursorAtPos.getColumnIndex(HostsDB.COLUMN_HOSTNAME));
		}

		/**
		 * Get the name of a host located at a position in the list.
		 *
		 * @param position Position in the list of the desired host.
		 * @return The name of the host located at the position in the list.
	 	 */
		public String getName(int position){
			Cursor cursorAtPos = (Cursor)getItem(position);
			return cursorAtPos.getString(
				cursorAtPos.getColumnIndex(HostsDB.COLUMN_NAME));
		}

		/**
		 * Refresh what is displayed in the adapter.
	   */
		public void refresh(){
			getCursor().requery();
		}
	}
}
